package org.springframework.security.web.context;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.springframework.core.log.LogMessage;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.util.Assert;
import org.springframework.web.filter.GenericFilterBean;

import java.io.IOException;

/**
 * @author Dillon
 * @date 2024/7/10
 * @slogan 致敬大师 致敬未来的你
 * @desc 缓存持久化过滤器 用户将缓存持久化到其他地方 磁盘 内存等
 * 通过 SecurityContextRepository 执行持久化
 * 当前类已废弃 {@link SecurityContextHolderFilter} 替代
 */
@Deprecated
public class SecurityContextPersistenceFilter extends GenericFilterBean {

	/**
	 * 标记请求是否已经经过当前过滤器
	 */
	static final String FILTER_APPLIED = "__spring_security_scpf_applied";

	/**
	 * 持久化接口
	 */
	private SecurityContextRepository repo;

	/**
	 * 缓存策略实现类 默认 threadLocal
	 */
	private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder.getContextHolderStrategy();

	/**
	 * 是否强制创建会话 默认不创建
	 */
	private boolean forceEagerSessionCreation = false;

	/**
	 * 构造函数
	 * 默认使用session作为持久化缓存
	 */
	public SecurityContextPersistenceFilter() {
		this(new HttpSessionSecurityContextRepository());
	}

	/**
	 * 构造函数
	 *
	 * @param repo 持久化缓存实现类
	 */
	public SecurityContextPersistenceFilter(SecurityContextRepository repo) {
		this.repo = repo;
	}

	/**
	 * 核心 过滤方法
	 *
	 * @param request  请求对象
	 * @param response 响应对象
	 * @param chain    过滤器链
	 * @throws IOException      执行异常
	 * @throws ServletException 执行异常
	 */
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		doFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);
	}

	/**
	 * 核心 过滤方法
	 *
	 * @param request  请求对象
	 * @param response 响应对象
	 * @param chain    过滤器链
	 * @throws IOException      执行异常
	 * @throws ServletException 执行异常
	 */
	private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		// 确保一次请求，该过滤器执行一次
		if (request.getAttribute(FILTER_APPLIED) != null) {
			chain.doFilter(request, response);
			return;
		}
		// 标记该过滤器一致性
		request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
		// 如果强制创建session，则调用getSession 强制创建一个空session（如果session未创建情况下）
		if (this.forceEagerSessionCreation) {
			HttpSession session = request.getSession();
			// 如果是新创建的session 日志记录
			if (this.logger.isDebugEnabled() && session.isNew()) {
				this.logger.debug(LogMessage.format("Created session %s eagerly", session.getId()));
			}
		}
		// 请求响应类包装
		HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);
		// 尝试从持久化中获取 上下文
		SecurityContext contextBeforeChainExecution = this.repo.loadContext(holder);
		try {
			// 设置到内存中
			this.securityContextHolderStrategy.setContext(contextBeforeChainExecution);
			// 空认证对象日志记录
			if (contextBeforeChainExecution.getAuthentication() == null) {
				logger.debug("设置一个空的上下文");
			} else {
				// 日志记录
				if (this.logger.isDebugEnabled()) {
					this.logger.debug(LogMessage.format("上下文设置到 %s", contextBeforeChainExecution));
				}
			}
			// 执行过滤器
			chain.doFilter(holder.getRequest(), holder.getResponse());
		} finally {
			// 情况上下文并将上下文执行持久化
			SecurityContext contextAfterChainExecution = this.securityContextHolderStrategy.getContext();
			// Crucial removal of SecurityContextHolder contents before anything else.
			this.securityContextHolderStrategy.clearContext();
			this.repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());
			request.removeAttribute(FILTER_APPLIED);
			this.logger.debug("请求完成 清空上下文");
		}
	}

	/**
	 * 设置是否强制创建session会话
	 *
	 * @param forceEagerSessionCreation 设置值
	 */
	public void setForceEagerSessionCreation(boolean forceEagerSessionCreation) {
		this.forceEagerSessionCreation = forceEagerSessionCreation;
	}

	/**
	 * 设置自定义的缓存实现策略类
	 *
	 * @param securityContextHolderStrategy 缓存实现策略类
	 */
	public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
		Assert.notNull(securityContextHolderStrategy, "securityContextHolderStrategy cannot be null");
		this.securityContextHolderStrategy = securityContextHolderStrategy;
	}

}
